home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-20 / nrd34.zip / NRDUTIL.PAS < prev    next >
Pascal/Delphi Source File  |  1991-06-01  |  51KB  |  1,756 lines

  1. {$I-}
  2. {$V-}
  3.  
  4. unit nrdutil;
  5.  
  6. interface
  7.   uses crt, dos, graph, screen, nrdio, async;
  8.  
  9.   type  prompttype    = (PAGE1, PAGE2);
  10.  
  11.   const
  12.       LINES       = 25;
  13.       CHARPERLINE = 80;
  14.       BACKTAB     = chr(10);
  15.       TAB         = chr(9);
  16.       PAGEUP      = chr(3);
  17.       PAGEDOWN    = chr(4);
  18.       UP          = chr(15);
  19.       DOWN        = chr(11);
  20.       RIGHTARROW  = chr(21);
  21.       LEFTARROW   = chr(7);
  22.       CTRLPAGEUP  = chr(14);
  23.       CTRLPAGEDN  = chr(16);
  24.       HOMEKY      = chr(5);
  25.       ENDKY       = chr(6);
  26.       MAP_OFFSET  = 2.0; { frequency offset from center for sync det. }
  27.  
  28.   { Receiver window screen limits }
  29.       REC_WIN_X_TOP    = 1;
  30.       REC_WIN_Y_TOP    = 1;
  31.       REC_WIN_X_BOTTOM = 79;
  32.       REC_WIN_Y_BOTTOM = 5;
  33.  
  34.       REVISION         = '3.4';
  35.  
  36.       PLOTBUFSIZE = 1024;
  37.  
  38.   type  buffer_type = array[0..PLOTBUFSIZE] of byte;
  39.  
  40.   var plot_buffer: buffer_type;
  41.       x_pos, y_pos:integer;
  42.       prompt_num:prompttype;
  43.       enable_s_meter:boolean;
  44.       meter_reading:integer;
  45.       graphmode:integer;
  46.       display_page:integer;
  47.       update_receiver_display:boolean;
  48.       last_log:integer;  { used for hot keying between active and last log }
  49.       last_log_data:logtype;{ used to copy data from last log to active log }
  50.       i,cnt: integer;
  51.       ch:char;
  52.       s:string;
  53.       oldstat:receivertype;
  54.       logentry:logtype;
  55.       min_mark,max_mark:word;
  56.       rslt:integer;
  57.       displayed_freq:array[1..LINES] of real;
  58.       displayed_lines:integer;
  59.       map:boolean;
  60.  
  61.   procedure sort(var data:sort_array_type; var index:recarraytype;
  62.                start, points:integer);
  63.  
  64.   procedure editfield(x,y,fieldlen:integer; number:boolean;
  65.                       var tabkey, backtabkey:boolean; var val:lstring);
  66.  
  67.   procedure draw_display_titles;
  68.   procedure top_window;
  69.   procedure bottom_window;
  70.   procedure init_rec_window;
  71.   procedure status_window;
  72.   function precess(var rec:integer; cnt:integer):boolean;
  73.   function bandwidth_to_str(bandwidth:bandwidthtype):short_str;
  74.   function mode_to_str(mode:modetype):short_str;
  75.   function agc_to_str(agc:agctype):short_str;
  76.   procedure show_log(rec:integer; refresh_screen,highlight:boolean);
  77.   { refresh_screen = TRUE; paint entire screen with log entries
  78.                    = FALSE then highlight line if indicated }
  79.   procedure write_prompt(s:string);
  80.   procedure cmd_prompt(prompt_num:prompttype);
  81.   procedure do_help;
  82.   procedure timed_s_meter;
  83.   procedure show_receiver;
  84.   procedure inc_mode;
  85.   procedure dec_mode;
  86.   procedure inc_bandwidth;
  87.   procedure dec_bandwidth;
  88.   procedure init_crt;
  89.   procedure graph_init;
  90.   procedure do_graph;
  91.  
  92. implementation
  93.  
  94.   procedure sort;
  95.   (* THIS PROCEDURE IMPLEMENTS 'QUICKSORT' BY C.A.R. HOARE. THIS N*LOG(N) *)
  96.   (* ALGORITHM IS A PARTITION EXCHANGE SORT AND IS DOCUMENTED IN THE 7-80 *)
  97.   (* ISSUE OF 'MICRO'.                                                    *)
  98.  
  99.   TYPE STACKTYPE = RECORD
  100.        UPPER:1..MAXREC; (*STORAGE FOR UPPER SEARCH RANGE*)
  101.        LOWER:1..MAXREC    (*   "     "  LOWER    "     "  *)
  102.      END;
  103.  
  104.   VAR P,Q (*CURRENT LOWER & UPPER INDEX BOUNDS TO BE SORTED. DATA[P] IS
  105.           USED AS A COMPARISON KEY IN THE SORTING PROCESS.           *)
  106.        ,I   (*STARTS AT P & IS INCREMENTED UNTIL DATA[I]>=DATA[P].       *)
  107.        ,J   (*STARTS AT Q & IS DECREMENTED UNTIL DATA[I]<=DATA[P].       *)
  108.        ,STACKPTR:INTEGER;
  109.     STACK:PACKED ARRAY[1..MAXREC] OF STACKTYPE; (*SEARCH RANGE STORAGE.*)
  110.     TEMPDATA,VALUE (*DATA[P]*):short_str;
  111.     TEMPINDEX:1..MAXREC;
  112.   BEGIN
  113.     P:=start; Q:=POINTS + start - 1; STACKPTR:=0;
  114.     REPEAT
  115.       WHILE P < Q DO
  116.       BEGIN
  117.         VALUE:=DATA[P]; I:=P; J:=Q + 1;
  118.         REPEAT
  119.           REPEAT J:=J - 1 UNTIL DATA[J]<=VALUE;
  120.           REPEAT inc(I)   UNTIL DATA[I]>=VALUE;
  121.           IF J > I THEN
  122.         BEGIN
  123.           TEMPDATA:=DATA[I]; TEMPINDEX:=INDEX[I];
  124.           DATA[I] :=DATA[J]; INDEX[I] :=INDEX[J];
  125.           DATA[J] :=TEMPDATA;INDEX[J] :=TEMPINDEX
  126.         END;
  127.         UNTIL J <= I;
  128.         TEMPINDEX:=INDEX[P];
  129.         DATA[P]:=DATA[J]; INDEX[P]:=INDEX[J];
  130.         DATA[J]:=VALUE;   INDEX[J]:=TEMPINDEX;
  131.         inc(STACKPTR);
  132.         IF J - P < Q - J
  133.           THEN
  134.         WITH STACK[STACKPTR] DO
  135.           BEGIN LOWER:=J + 1; UPPER:=Q; Q:=J - 1
  136.           END
  137.           ELSE
  138.         WITH STACK[STACKPTR] DO
  139.           BEGIN LOWER:=P; UPPER:=J - 1; P:=J + 1
  140.           END;
  141.       END; (*WHILE*)
  142.     write(output,'.');
  143.     IF STACKPTR > 0 THEN (*GRAB NEW SEARCH RANGE OFF STACK*)
  144.       WITH STACK[STACKPTR] DO BEGIN Q:=UPPER; P:=LOWER END;
  145.     STACKPTR:=STACKPTR-1
  146.     UNTIL STACKPTR < 0 (*EMPTY STACK*)
  147.   END;
  148.  
  149.   procedure draw_display_titles;
  150.   begin
  151.     top_window;
  152.     gotoxy(1,5);
  153.     clreol;
  154.     case display_page of
  155.       1: begin
  156.            gotoxy(2,5);
  157.            write(output,'Num');
  158.            gotoxy(7,5);
  159.            write(output,'Date');
  160.            gotoxy(13,5);
  161.            write(output,'Strt');
  162.            gotoxy(18,5);
  163.            write(output,'End');
  164.            gotoxy(24,5);
  165.            write(output,'Freq');
  166.            gotoxy(32,5);
  167.            write(output,'Station ID');
  168.            gotoxy(52,5);
  169.            write(output,'Location');
  170.          end;
  171.       2: begin
  172.            gotoxy(3,5);
  173.            write(output,'Freq');
  174.            gotoxy(11,5);
  175.            write(output,'Comment');
  176.          end;
  177.       3: begin
  178.            gotoxy(2,5);
  179.            write(output,'Num');
  180.            gotoxy(7,5);
  181.            write(output,'Date');
  182.            gotoxy(13,5);
  183.            write(output,'Strt');
  184.            gotoxy(18,5);
  185.            write(output,'End');
  186.            gotoxy(24,5);
  187.            write(output,'Freq');
  188.            gotoxy(32,5);
  189.            write(output,'Mode');
  190.            gotoxy(39,5);
  191.            write(output,'BW');
  192.            gotoxy(43,5);
  193.            write(output,'AGC');
  194.            gotoxy(48,5);
  195.            write(output,'Attn');
  196.          end;
  197.     end;
  198.   end;
  199.  
  200.   procedure editfield;
  201.  
  202.   { parameters: x,y     = cursor position
  203.         fieldlen = allowable length for field
  204.         number     = flag that if true restricts the keys usable
  205.         val     = return string }
  206.  
  207.   var ptr,i:integer;
  208.       ch:char;
  209.       errflg,flag,insert_mode:boolean;
  210.       oldval:string[255];
  211.  
  212.     procedure show_line(x,y,fieldlen:integer; val:lstring; edit:boolean);
  213.     var i:integer;
  214.         lf_bracket,rt_bracket:char;
  215.     begin { show_line }
  216.       gotoxy(x,y + 1);
  217.       if edit then { they are editing this line }
  218.         begin
  219.       lf_bracket:='[';
  220.           rt_bracket:=']';
  221.       writea(RED,BACKGROUND);
  222.       writea(BLACK,FOREGROUND);
  223.         end
  224.       else
  225.         begin
  226.       writea(CYAN,BACKGROUND);
  227.       writea(BLACK,FOREGROUND);
  228.           lf_bracket:=' ';
  229.       rt_bracket:=' ';
  230.         end;
  231.       write(lf_bracket,val);
  232.       gotoxy(x + 1 + fieldlen,y + 1);
  233.       write(rt_bracket);
  234.     end; { show_line }
  235.  
  236.     procedure blankfill(fieldlen:integer; var val:lstring);
  237.     begin
  238.       while length(val) < fieldlen do val:=concat(val,' ');
  239.     end;
  240.  
  241.  
  242.     procedure get_normal;
  243.  
  244.     { fetch normal character and display it.  Handle field overflow and
  245.       insert_mode }
  246.  
  247.     var i:integer;
  248.     s:string[1];
  249.     begin
  250.       if ptr < fieldlen
  251.     then
  252.       begin
  253.         if ((number) and (ch in ['0'..'9',' ','.']))
  254.         or ((ptr = 0) and (ch = '-')) or not number then
  255.           begin { all's well }
  256.         if not insert_mode then
  257.           begin
  258.             write(ch); { echo character to screen }
  259.             ptr:=ptr + 1;
  260.             val[ptr]:=ch
  261.           end
  262.         else { handle insert mode }
  263.           begin
  264.             inc(ptr);
  265.             for i:=fieldlen downto ptr do val[i]:=val[i - 1];
  266.             val[ptr]:=ch;
  267.             for i:=ptr to fieldlen do write(val[i]);
  268.             gotoxy(x + ptr + 1,y + 1);
  269.           end
  270.           end
  271.       end
  272.     end; { get_normal }
  273.  
  274.     procedure do_backspace;
  275.     begin
  276.       if ptr > 0 then { it's ok to backspace }
  277.     begin
  278.       ptr:=ptr - 1;
  279.       gotoxy(x + ptr + 1,y + 1)
  280.     end
  281.     end;
  282.  
  283.     procedure do_forwardspace;
  284.     begin
  285.       if ptr < fieldlen then { its ok to forward space }
  286.     begin
  287.       inc(ptr);
  288.       gotoxy(x + ptr + 1,y + 1)
  289.     end
  290.     end;
  291.  
  292.     procedure do_del;
  293.     var i:integer;
  294.     begin
  295.       for i:=ptr + 1 to fieldlen - 1 do val[i]:=val[i + 1];
  296.       val[fieldlen]:=' ';
  297.       for i:=ptr + 1 to fieldlen do write(val[i]);
  298.       gotoxy(x + ptr + 1,y + 1);
  299.     end;
  300.  
  301.     procedure do_rub;
  302.     var i:integer;
  303.     begin
  304.       if ptr > 0 then ptr:=ptr - 1;
  305.       gotoxy(x + ptr + 1,y + 1);
  306.       do_del;
  307.     end;
  308.  
  309.     procedure toggle_insert;
  310.     begin
  311.       insert_mode:=not insert_mode;
  312.    end;
  313.  
  314.    procedure do_home;
  315.    begin
  316.      ptr:=0;
  317.      gotoxy(x + ptr + 1,y + 1)
  318.    end;
  319.  
  320.    procedure do_end;
  321.    begin
  322.      ptr:=fieldlen;
  323.      while (ptr > 0) and (val[ptr] = ' ') do
  324.        ptr:=ptr - 1;
  325.      gotoxy(x + ptr + 1,y + 1)
  326.    end;
  327.  
  328.    procedure do_tab;
  329.    begin
  330.      tabkey:=TRUE;
  331.      ch:=chr(13);
  332.    end;
  333.  
  334.    procedure do_backtab;
  335.    begin
  336.      backtabkey:=TRUE;
  337.      ch:=chr(13);
  338.    end;
  339.  
  340.   begin { editfield }
  341.     insert_mode:=FALSE;
  342.     tabkey:=FALSE;
  343.     backtabkey:=FALSE;
  344.     if number and (val = '-0.00') then val:='0.00';
  345.     oldval:=val; { save copy in case they abort }
  346.     if length(val) > fieldlen then val:=copy(val,1,fieldlen);
  347.     blankfill(fieldlen,val);
  348.     ptr:=0;
  349.     show_line(x,y,fieldlen,val,TRUE);
  350.     gotoxy(x + 1,y + 1);
  351.     repeat
  352.       ch:=fetch;
  353.       if (ch <> chr(13)) and (ord(ch) >= ord(' ')) then get_normal
  354.       else if ch = keyinfo.bskey   then do_backspace
  355.       else if ch = keyinfo.fskey   then do_forwardspace
  356.       else if ch = keyinfo.delkey  then do_del
  357.       else if ch = keyinfo.rubkey  then do_rub
  358.       else if ch = keyinfo.inskey  then toggle_insert
  359.       else if ch = keyinfo.homekey then do_home
  360.       else if ch = keyinfo.endkey  then do_end
  361.       else if ch = keyinfo.tabkey  then do_tab
  362.       else if ch = keyinfo.backtabkey then do_backtab
  363.     until (ch = chr(13)) or (ch = keyinfo.esckey);
  364.  
  365.     if number then { strip off trailing blanks }
  366.       begin
  367.     ptr:=length(val);
  368.     flag:=TRUE;
  369.     while (ptr > 0) and flag do
  370.       begin
  371.         flag:=val[ptr] = ' ';
  372.         if flag then delete(val,ptr,1);
  373.         ptr:=ptr - 1;
  374.       end;
  375.       end;
  376.     show_line(x,y,fieldlen,val,FALSE);
  377.   end; { editfield }
  378.  
  379.   procedure top_window;
  380.   begin
  381.     window(REC_WIN_X_TOP,REC_WIN_Y_TOP,REC_WIN_X_BOTTOM+1,REC_WIN_Y_BOTTOM);
  382.   end;
  383.  
  384.   procedure bottom_window;
  385.   begin
  386.     window(1,REC_WIN_Y_BOTTOM + 1, 80,24);
  387.     gotoxy(x_pos,y_pos);
  388.   end;
  389.  
  390.   procedure init_rec_window;
  391.  
  392.     procedure draw_box(width,hieght:integer);
  393.     const TL = chr(201);
  394.           TR = chr(187);
  395.           BL = chr(200);
  396.           BR = chr(188);
  397.           HZ = chr(205);
  398.           VT = chr(186);
  399.  
  400.     var i:integer;
  401.  
  402.       procedure draw_horiz;
  403.       var i:integer;
  404.       begin
  405.         for i:=1 to width - 2 do write(output,HZ);
  406.       end;
  407.  
  408.     begin
  409.       { draw top }
  410.       gotoxy(1,2);
  411.       write(output,TL);
  412.       draw_horiz;
  413.       write(output,TR);
  414.  
  415.       { draw sides }
  416.       for i:=1 to hieght - 2 do
  417.         begin
  418.           gotoxy(1,i + 2);      write(output,VT);
  419.           gotoxy(width, i + 2); write(output,VT);
  420.         end;
  421.  
  422.       { draw bottom }
  423.       gotoxy(1,hieght + 1);
  424.       write(output,BL);
  425.       draw_horiz;
  426.       write(output,BR);
  427.     end;
  428.  
  429.   begin
  430.     top_window;
  431.     writea(BLACK,BACKGROUND);
  432.     home;
  433.     cmd_prompt(prompt_num);
  434.     writea(BROWN,FOREGROUND);
  435.     draw_box(REC_WIN_X_BOTTOM - REC_WIN_X_TOP + 1,
  436.              REC_WIN_Y_BOTTOM - REC_WIN_Y_TOP - 1) ;
  437.     gotoxy(30,2);
  438.     writea(LIGHTGRAY,FOREGROUND);
  439.     if radio_type = 525
  440.        then write(output,'NRD 525 Status')
  441.        else write(output,'NRD 535 Status');
  442.     gotoxy(3,3);
  443.     write(output,'Mode:');
  444.     gotoxy(15,3);
  445.     write(output,'BW:');
  446.     gotoxy(29,3);
  447.     write(output,'AGC:');
  448.     if radio_type = 525 then
  449.       begin
  450.         gotoxy(42,3);
  451.         write(output,'Ch:');
  452.       end;
  453.     gotoxy(54,3);
  454.     write(output,'Freq:');
  455.     gotoxy(68,3);
  456.     write(output,' khz');
  457.     display_page:=1;
  458.     writea(LIGHTGRAY,FOREGROUND);
  459.     draw_display_titles;
  460.     writea(LIGHTGRAY,FOREGROUND);
  461.   end;
  462.  
  463.   procedure status_window;
  464.   begin
  465.     window(1,25,80,25);
  466.     gotoxy(1,1);
  467.     clreol;
  468.     writea(BLACK,BACKGROUND);
  469.     writea(LIGHTGRAY, FOREGROUND);
  470.     write(output,'Active Log: ');
  471.     writea(CYAN, FOREGROUND);
  472.     write(output,loglist.log[loglist.currentlog].logname);
  473.     if last_log <> 0 then
  474.       begin
  475.         writea(LIGHTGRAY, FOREGROUND);
  476.         write(output,'  Inactive Log: ');
  477.         writea(CYAN, FOREGROUND);
  478.         write(output,loglist.log[last_log].logname);
  479.       end;
  480.   end;
  481.  
  482.   function mode_to_str;
  483.   var s:short_str;
  484.   begin
  485.     case mode of
  486.       RTTY:     s:='RTTY';
  487.       CW:       s:=' CW';
  488.       USB:      s:='USB';
  489.       LSB:      s:='LSB';
  490.       AM:       s:=' AM';
  491.       FM:       s:=' FM';
  492.       FAX:      s:='FAX';
  493.       ECSS_USB: s:='ECSSu';
  494.       ECSS_LSB: s:='ECSSl';
  495.     end;
  496.     mode_to_str:=s;
  497.   end;
  498.  
  499.   function bandwidth_to_str;
  500.   var s:short_str;
  501.   begin
  502.     case bandwidth of
  503.       NARR:  s:=' NARR';
  504.       INTER: s:='INTER';
  505.       WIDE:  s:=' WIDE';
  506.       AUX:   s:=' AUX';
  507.     end;
  508.     bandwidth_to_str:=s;
  509.   end;
  510.  
  511.   function agc_to_str;
  512.   var s:short_str;
  513.   begin
  514.     case agc of
  515.       FAST: s:='FAST';
  516.       SLOW: s:='SLOW';
  517.       OFF:  s:=' OFF';
  518.     end;
  519.     agc_to_str:=s;
  520.   end;
  521.  
  522.   procedure show_log_line(logdata:logtype;rec,i:word);
  523.  
  524.     procedure show_line1;
  525.     begin
  526.       write(output,rec:4);
  527.       with logdata do
  528.         begin
  529.           gotoxy(6,i);
  530.           write(output,date);
  531.           gotoxy(13,i);
  532.           write(output,begin_time);
  533.           gotoxy(18,i);
  534.           write(output,end_time);
  535.           gotoxy(23,i);
  536.           write(output,frequency:8:2);
  537.           gotoxy(32,i);
  538.           write(output,callsign);
  539.           gotoxy(52,i);
  540.           write(output,location);
  541.         end;
  542.     end;
  543.  
  544.     procedure show_line2;
  545.     begin
  546.       with logdata do
  547.         begin
  548.           gotoxy(2,i);
  549.           write(output,frequency:8:2);
  550.           gotoxy(11,i);
  551.           write(output,comment);
  552.         end;
  553.     end;
  554.  
  555.     procedure show_line3;
  556.     var s:short_str;
  557.     begin
  558.       write(output,rec:4);
  559.       with logdata do
  560.         begin
  561.           gotoxy(6,i);
  562.           write(output,date);
  563.           gotoxy(13,i);
  564.           write(output,begin_time);
  565.           gotoxy(18,i);
  566.           write(output,end_time);
  567.           gotoxy(23,i);
  568.           write(output,frequency:8:2);
  569.           gotoxy(32,i);
  570.           s:=mode_to_str(mode);
  571.           write(output,s);
  572.           gotoxy(37,i);
  573.           s:=bandwidth_to_str(bandwidth);
  574.           write(output,s);
  575.           gotoxy(43,i);
  576.           s:=agc_to_str(agc);
  577.           write(output,s);
  578.           gotoxy(49,i);
  579.           case attenuator of
  580.             YES: write(output,'ON');
  581.             NO:  write(output,'OFF');
  582.           end;
  583.         end;
  584.     end;
  585.  
  586.   begin
  587.     gotoxy(1,i); clreol;
  588.     case display_page of
  589.       1: show_line1;
  590.       2: show_line2;
  591.       3: show_line3;
  592.     end;
  593.   end;
  594.  
  595.   function precess;
  596.   { skip cnt displayed records; return TRUE is not past eof }
  597.   var i:integer;
  598.   begin
  599.     for i:=1 to cnt do
  600.       begin
  601.         rec:=rec + 1;
  602.         while (rec < records)
  603.           and (recdata.recstat[recdata.recptr[rec]] <> SHOW) do
  604.             rec:=rec + 1;
  605.       end;
  606.     if rec > records then rec:=records;
  607.     precess:=recdata.recstat[recdata.recptr[rec]] = SHOW;
  608.   end;
  609.  
  610.   procedure show_log;
  611.   { refresh_screen = TRUE; paint entire screen with log entries
  612.                    = FALSE then highlight line if indicated }
  613.   var i,j,x_temp,y_temp:integer;
  614.       logdata:logtype;
  615.   begin
  616.     writea(CYAN,FOREGROUND); writea(BLACK,BACKGROUND);
  617.     i:=0; j:=rec - 1;
  618.     y_temp:=wherey; { used to highlight cursor line }
  619.     x_temp:=wherex;
  620.     if refresh_screen then home;
  621.     while (i < LINES - REC_WIN_Y_BOTTOM - 1) and (j < records) do
  622.       begin
  623.         inc(i);
  624.         if precess(j,1) then
  625.           begin
  626.             get_log(logbuf,logdata,recdata.recptr[j]);
  627.             displayed_freq[i]:=logdata.frequency;
  628.             if (j >= min_mark) and (j <= max_mark) then
  629.               begin
  630.                 writea(BLACK,FOREGROUND);
  631.                 writea(YELLOW,BACKGROUND);
  632.                 show_log_line(logdata,j,i);
  633.                 writea(BLACK,BACKGROUND);
  634.                 writea(CYAN,FOREGROUND);
  635.               end
  636.             else if (i = y_temp) and highlight then
  637.               begin
  638.                 writea(BLACK,FOREGROUND);
  639.                 writea(CYAN,BACKGROUND);
  640.                 show_log_line(logdata,j,i);
  641.                 writea(BLACK,BACKGROUND);
  642.                 writea(CYAN,FOREGROUND);
  643.               end
  644.             else if refresh_screen or ((i = y_temp) and not highlight)
  645.               then show_log_line(logdata,j,i);
  646.           end;
  647.       end;
  648.     displayed_lines:=i;
  649.     gotoxy(x_temp,y_temp);
  650.   end;
  651.  
  652.   procedure write_prompt;
  653.  
  654.   { write green prompt at top of top window; leave with light gray foreground
  655.     and in top window }
  656.  
  657.   begin
  658.     top_window;
  659.     writea(GREEN,FOREGROUND);
  660.     writea(BLACK,BACKGROUND);
  661.     gotoxy(1,1);
  662.     clreol;
  663.     write(output,s);
  664.     writea(LIGHTGRAY,FOREGROUND);
  665.   end;
  666.  
  667.   procedure cmd_prompt;
  668.   const T0 = 'NRD'+REVISION;
  669.         T1 = ': L(og, C(onfirm, S(ort, E(dit, T(une, P(age, H(elp, ';
  670.         T2 = 'Q(uit [/]';
  671.         T3 = 's-meteR, ';
  672.   begin
  673.     case prompt_num of
  674.       PAGE1: if has_map then write_prompt(T0+T1+'K(iwa, '+T2)
  675.              else if radio_type = 525 then write_prompt(T0+T1+T2)
  676.              else write_prompt(T0+T1+T3+T2);
  677.       PAGE2:write_prompt(T0+
  678.   ': D(elete, uN(delete, M(ark, U(nmark, J(ournal, A(lternate, W(rite [/]');
  679.     end;
  680.   end;
  681.  
  682.   procedure do_help;
  683.   var ch:char;
  684.  
  685.     procedure help_commands;
  686.     var ch:char;
  687.     begin
  688.       write_prompt('Help -- Receiver display  <Hit any key to return>');
  689.       window(1,REC_WIN_Y_BOTTOM + 1, 80,25);
  690.       home;
  691.       writeln(output,
  692. 'Commands are designed to be easy to learn and use.  All commands are');
  693.       writeln(output,
  694. 'activated with a single key and are spelled out on the command line.  For');
  695.       writeln(output,
  696. 'example, to log a station, hit "L" (shows up as "L(og" on prompt).');
  697.       writeln(output);
  698.       writeln(output,'Command Summary:');
  699.       writeln(output);
  700.       writeln(output,
  701. '/: Toggles command menu (commands from both menus are always active)');
  702.       writeln(output,
  703. 'L: Log (creates new entry in log, receiver contents automatically included)'
  704.           );
  705.       writeln(output,
  706. 'C: Confirm (updates time, date, receiver contents for highlighted entry)');
  707.       writeln(output,
  708. 'S: Sort data base with 2 sort keys');
  709.       writeln(output,
  710. 'E: Edit field where cursor is located.  Hit ENTER or a TAB key when done');
  711.       writeln(output,
  712. 'T: Tune receiver to highlighted entry.  Updates all receiver parameters');
  713.       writeln(output,
  714. 'P: Page right (the contents for an entry span 3 pages, faster than tabs)');
  715. writeln(output,'D: Deletes a log entry');
  716.       writeln(output,
  717. 'N: uNdeletes a log entry (logging new stations reuses deleted space)');
  718.      writeln(output,
  719. 'M: Mark line(s) for writing or moving to other datalogs (see Journal)');
  720.       writeln(output,'U: Unmark lines');
  721.       writeln(output,'A: Alternates between Active and Inactive Logs');
  722.       writeln(output,'W: Write entry from Inactive Log to Active Log');
  723. write(output,'Q: Quits the program');
  724.  
  725.       ch:=fetch;
  726.       home;
  727.     end;
  728.  
  729.     procedure help_more;
  730.     var ch:char;
  731.     begin
  732.       write_prompt('Help -- More Commands  <Hit any key to return>');
  733.       bottom_window;
  734.       home;
  735.       writeln(output,
  736. 'J: Journal: allows you to select other data logs and do things with them.');
  737.       writeln(output,
  738. '         I keep multiple logs -- a music log for stations that play');
  739.       writeln(output,
  740. '         interesting music and target logs for areas I''m trying to get.');
  741.       writeln(output,
  742. '         Target logs allow you to scan what''s there VERY quickly.');
  743.       writeln(output,
  744. '         The Write command allows marked areas to be moved from'
  745.           );
  746.       writeln(output,
  747. '         one database to another; like when you find one of those targets!'
  748.           );
  749.       writeln(output,
  750. '         Move is like a write but deletes the marked entry.  Print writes');
  751.       writeln(output,
  752. '         the selected database to your printer.  Import will copy data');
  753.       writeln(output,
  754. '         from Tom Sundstrom''s English Language SW Broadcast Schedules to');
  755.       writeln(output,
  756. '         this program format.  You can order these from Tom (609) 859-2447.'
  757.           );
  758.       if has_map then
  759.         begin
  760.           writeln(output,
  761. 'K: KIWA (this only applies if you have a KIWA Map unit)  "K" toggles KIWA');
  762.           writeln(output,
  763. '         mode.  When enabled, the receiver is placed in AM and the radio');
  764.           writeln(output,
  765. '         is detuned a couple of Khz for good fidelity.  Stations logged or'
  766.           );
  767.           writeln(output,
  768. '         confirmed will be rounded to the nearest 5 Khz.  Disabling puts');
  769.           writeln(output,
  770. '         the radio in ECS mode with the appropriate sideband selected based'
  771.           );
  772.           writeln(output,
  773. '         on the offset.  The MAP unit provides synchronous detection for a'
  774.           );
  775.           writeln(output,
  776. '         525 and was described in Guy Atkin''s 9/90 NASWA article p.18.  To'
  777.           );
  778.           writeln(output,
  779. '         tell the program you have a MAP, delete the config.dat file and');
  780.           write(output,
  781. '         rerun program.');
  782.         end;
  783.       if radio_type = 535 then
  784.         begin
  785.           writeln(output,
  786. 'R: s-meteR: Toggles mode of periodically updating computer S-Meter display.'
  787.           );
  788.           writeln(output,
  789. '         Unfortunately, reading the S-Meter can cause annoying synthesizer'
  790.           );
  791.           writeln(output,
  792. '         re-locking noise in LSB, USB, CW, and RTTY modes.  Use this mode');
  793.           writeln(output,
  794. '         to disable this when it bothers you');
  795.         end;
  796.       ch:=fetch;
  797.     end;
  798.  
  799.     procedure help_receiver;
  800.     var ch:char;
  801.     begin
  802.       write_prompt('Help -- Receiver display  <Hit any key to return>');
  803.       bottom_window;
  804.       home;
  805.       gotoxy(1,3);
  806.       writeln(output,
  807. 'The box labled "NRD',radio_type
  808. ,' Status" contains the last sampled receiver status.'
  809.           );
  810.       writeln(output,
  811. 'Mostly, this is self-explanatory.  BW = Bandwidth, Freq is the receiver'
  812.           );
  813.       writeln(output,
  814. 'frequency.  If the attenuator is active, you will see "ATT" at the right'
  815.           );
  816.       writeln(output,
  817. 'of the screen.  If you said you had a KIWA Map unit and it is active, you'
  818.           );
  819.       writeln(output,
  820. 'will see a "K" at the far right of the status box.  The KIWA features are'
  821.           );
  822.       writeln(output,
  823. 'described in the commands section.  If you don''t have one, don''t worry'
  824.           );
  825.       writeln(output,
  826. 'about this.  Normally, the status is displayed in CYAN unless something'
  827.           );
  828.       writeln(output,
  829. 'has changed since the last sample.  Changes are displayed in RED.  To cause'
  830.           );
  831.       writeln(output,
  832. 'the receiver status to be sampled, hit any non-command key (like space).'
  833.           );
  834.       writeln(output,
  835. 'This approach to updating the display was chosen deliberately to keep the'
  836.           );
  837.       write(output,
  838. 'radio "unlocked" so you can punch up commands on the radio.');
  839.       ch:=fetch;
  840.     end;
  841.  
  842.     procedure help_other;
  843.     var ch:char;
  844.     begin
  845.       write_prompt('Help -- Receiver display  <Hit any key to return>');
  846.       bottom_window;
  847.       home;
  848.       gotoxy(1,3);
  849.       writeln(output,
  850. 'There are other useful keys not covered in the command section.  First off,'
  851.           );
  852.       writeln(output,
  853. 'all the normal cursor commands work including tabs.  HOME takes you to the'
  854.           );
  855.       writeln(output,
  856. 'top of the display log, END takes you to the bottom.  Control-PAGE keys'
  857.           );
  858.       writeln(output,
  859. 'work like PAGE keys, only 10 pages at a time.  "+" and "-" keys bump the'
  860.           );
  861.       writeln(output,
  862. 'frequency up or down 5 Khz.  If you are in USB or LSB mode, the program'
  863.           );
  864.       writeln(output,
  865. 'assumes you are using ECS detection and tunes off 1 Khz for a fraction of'
  866.           );
  867.       writeln(output,
  868. 'a second before tuning in the correct frequency.  This feature was added to'
  869.           );
  870.       writeln(output,
  871. 'hear the heterodyne of weak stations you might miss while rapidly scanning.'
  872.           );
  873.       writeln(output,
  874. 'The "<" and ">" keys (or the "," and "." keys so no shifting is needed)'
  875.           );
  876.       writeln(output,
  877. 'decrement or increment the receiver mode.  Similarly, the "[" and "]" keys'
  878.           );
  879.       writeln(output,
  880. 'bump the receiver bandwidth selection.  "*" will find the closest');
  881.       writeln(output,
  882. 'log entry for the currently tuned frequency');
  883.       writeln(output);
  884.       writeln(output,
  885. 'The offset from GMT to your computer''s time is stored in the "CONFIG.DAT"'
  886.           );
  887.       writeln(output,
  888. 'file.  If this is wrong, delete CONFIG.DAT and the program will prompt you'
  889.           );
  890.       write(output,
  891. 'for the information to correct.');
  892.       ch:=fetch;
  893.     end;
  894.  
  895.   begin
  896.     repeat
  897.       bottom_window;
  898.       home;
  899.       gotoxy(1,8);
  900.       writeln(output,
  901. 'Type letter for command.  For example, to learn more about the receiver');
  902.       writeln(output,
  903. 'display, type "r".  Type "q" to return from the help facility.');
  904.       write_prompt(
  905. 'Help: R(eceiver display, C(ommands, M(ore commands, O(ther, Q(uit');
  906.       ch:=upcase(fetch);
  907.       case ch of
  908.         'R': help_receiver;
  909.         'C': help_commands;
  910.         'M': help_more;
  911.         'O': help_other;
  912.       end;
  913.     until ch = 'Q';
  914.     cmd_prompt(prompt_num);
  915.     bottom_window;
  916.   end;
  917.  
  918.   procedure timed_s_meter;
  919.   var reading:integer;
  920.   begin
  921.     x_pos:=wherex; y_pos:=wherey;
  922.     if radio_type <> 535 then exit;
  923.     check_s_meter(reading);
  924.     top_window;
  925.     gotoxy(42,3);
  926.     writea(LIGHTGRAY,FOREGROUND);
  927.     write(output,'S-Meter:');
  928.     if (reading > 9)
  929.       then writea(RED,FOREGROUND)
  930.       else writea(CYAN,FOREGROUND);
  931.     write(output,reading:2);
  932.     bottom_window;
  933.   end;
  934.  
  935.   procedure show_receiver;
  936.   var s:string;
  937.  
  938.     procedure do_out(unchanged:boolean; s:string);
  939.     begin
  940.       if not unchanged then writea(RED,FOREGROUND);
  941.       write(output,s);
  942.       writea(CYAN,FOREGROUND);
  943.     end;
  944.  
  945.   begin
  946.      x_pos:=wherex; y_pos:=wherey;
  947.     top_window;
  948.     writea(CYAN,FOREGROUND);
  949.     with receiverstat do
  950.       begin
  951.         gotoxy(9,3);
  952.         case mode of
  953.           RTTY:     s:='RTTY ';
  954.           CW:       s:='CW   ';
  955.           USB:      s:='USB  ';
  956.           LSB:      s:='LSB  ';
  957.           AM:       s:='AM   ';
  958.           FM:       s:='FM   ';
  959.           FAX:      s:='FAX  ';
  960.           ECSS_USB: s:='ECSSu';
  961.           ECSS_LSB: s:='ECSSl';
  962.         end;
  963.         do_out(receiverstat.mode = oldstat.mode,s);
  964.         gotoxy(19,3);
  965.         case bandwidth of
  966.           WIDE: s:='WIDE ';
  967.           INTER:s:='INTER';
  968.           NARR: s:='NARR ';
  969.           AUX:  s:='AUX  ';
  970.          end;
  971.         do_out(receiverstat.bandwidth = oldstat.bandwidth,s);
  972.          gotoxy(34,3);
  973.          case agc of
  974.            SLOW: s:='SLOW';
  975.            FAST: s:='FAST';
  976.            OFF:  s:='OFF ';
  977.          end;
  978.          do_out(receiverstat.agc = oldstat.agc,s);
  979.          case attenuator of
  980.            YES:  s:='ATT';
  981.            NO:   s:='   ';
  982.          end;
  983.          gotoxy(74,3);
  984.          do_out(receiverstat.attenuator = oldstat.attenuator,s);
  985.          if radio_type = 525 then
  986.            begin
  987.              gotoxy(46,3);
  988.              str(channel:3,s);
  989.              do_out(receiverstat.channel = oldstat.channel,s);
  990.            end;
  991.          gotoxy(60,3);
  992.          str(frequency:8:2,s);
  993.          do_out(receiverstat.frequency = oldstat.frequency,s);
  994.          gotoxy(78,3);
  995.          if map then write(output,'K') else write(output,' ');
  996.          writea(LIGHTGRAY,FOREGROUND);
  997.       end;
  998.     bottom_window;
  999.     oldstat:=receiverstat;
  1000.     gotoxy(x_pos,y_pos);
  1001.   end;
  1002.  
  1003.   procedure inc_mode;
  1004.   var x_pos,y_pos:integer;
  1005.   begin
  1006.     x_pos:=wherex; y_pos:=wherey;
  1007.     with receiverstat do
  1008.       begin
  1009.         remote_on;
  1010.         if radio_type = 525 then
  1011.           begin
  1012.             if mode < FAX
  1013.               then mode:=succ(mode)
  1014.               else mode:=RTTY;
  1015.           end
  1016.         else { do special case for nrd535 }
  1017.           begin
  1018.             case mode of
  1019.                   RTTY: mode:=CW;
  1020.                     CW: mode:=USB;
  1021.                    USB: mode:=LSB;
  1022.                    LSB: mode:=AM;
  1023.                     AM: mode:=ECSS_USB; { change order }
  1024.               ECSS_USB: mode:=ECSS_LSB;
  1025.               ECSS_LSB: mode:=FM;
  1026.                     FM: mode:=FAX;
  1027.                    FAX: mode:=RTTY;
  1028.             end;
  1029.           end;
  1030.         set_mode(mode);
  1031.         remote_off(REMOTE_DLY + 100);
  1032.         if radio_type = 535 then toggle_remote;
  1033.         show_receiver;
  1034.         gotoxy(x_pos,y_pos);
  1035.       end;
  1036.   end;
  1037.  
  1038.   procedure dec_mode;
  1039.   var x_pos,y_pos:integer;
  1040.   begin
  1041.     x_pos:=wherex; y_pos:=wherey;
  1042.     with receiverstat do
  1043.       begin
  1044.         remote_on;
  1045.         if radio_type = 525 then
  1046.           begin
  1047.             if mode > RTTY
  1048.               then mode:=pred(mode)
  1049.               else mode:=FAX;
  1050.           end
  1051.         else { do special case for nrd535 }
  1052.           begin
  1053.             case mode of
  1054.                   RTTY: mode:=FAX;
  1055.                     CW: mode:=RTTY;
  1056.                    USB: mode:=CW;
  1057.                    LSB: mode:=USB;
  1058.                     AM: mode:=LSB;
  1059.               ECSS_USB: mode:=AM;
  1060.               ECSS_LSB: mode:=ECSS_USB;
  1061.                     FM: mode:=ECSS_LSB;
  1062.                    FAX: mode:=FM;
  1063.             end;
  1064.           end;
  1065.         set_mode(mode);
  1066.         remote_off(REMOTE_DLY + 100);
  1067.         if radio_type = 535 then toggle_remote;
  1068.         show_receiver;
  1069.         gotoxy(x_pos,y_pos);
  1070.       end;
  1071.   end;
  1072.  
  1073.   procedure inc_bandwidth;
  1074.   begin
  1075.     with receiverstat do
  1076.       begin
  1077.         remote_on;
  1078.         bandwidth:=succ(bandwidth);
  1079.         set_bandwidth(bandwidth);
  1080.         remote_off(REMOTE_DLY);
  1081.         if radio_type = 535 then toggle_remote;
  1082.         show_receiver;
  1083.       end;
  1084.   end;
  1085.  
  1086.   procedure dec_bandwidth;
  1087.   begin
  1088.     with receiverstat do
  1089.       begin
  1090.         remote_on;
  1091.         bandwidth:=pred(bandwidth);
  1092.         set_bandwidth(bandwidth);
  1093.         remote_off(REMOTE_DLY);
  1094.         if radio_type = 535 then toggle_remote;
  1095.         show_receiver;
  1096.       end;
  1097.   end;
  1098.  
  1099.   procedure init_crt;
  1100.   begin
  1101.     home;
  1102.     init_rec_window;
  1103.     update_receiver_display:=TRUE;
  1104.     status_window;
  1105.     bottom_window;
  1106.   end;
  1107.  
  1108.   procedure graph_init;
  1109.   var graphdriver:integer;
  1110.       errorcode:integer;
  1111.   begin
  1112.     if radio_type = 525 then exit;
  1113.     graphdriver:=detect;
  1114.     case graphdriver of
  1115.       CGA:     graphmode:=CGAHI;
  1116.       MCGA:    graphmode:=MCGAHI;
  1117.       EGA:     graphmode:=EGAHI;
  1118.       EGA64:   graphmode:=EGA64HI;
  1119.       EGAMONO: graphmode:=EGAMONOHI;
  1120.       IBM8514: graphmode:=IBM8514HI;
  1121.       HERCMONO:graphmode:=HERCMONOHI;
  1122.       ATT400:  graphmode:=ATT400HI;
  1123.       VGA:     graphmode:=VGAHI;
  1124.       PC3270:  graphmode:=PC3270HI;
  1125.     end;
  1126.     initgraph(graphdriver,graphmode,'');
  1127.     errorcode:=graphresult;
  1128.     if errorcode <> grok then exit;
  1129.     restorecrtmode;
  1130.     home;
  1131.   end;
  1132.  
  1133.   procedure do_graph;
  1134.   const X_INIT    = 40;
  1135.         Y_INIT    = 43;
  1136.         SCALE     = 2.0;
  1137.         Y_SCALE   = 480;
  1138.         X_SCALE   = 640;
  1139.         X_AXIS    = X_INIT - 5;
  1140.  
  1141.         { constants needed for toggling between the log and a spectral plot }
  1142.         SPECTRAL_MODE: boolean = FALSE;
  1143.         start_freq: real = 0.0;
  1144.         stop_freq:  real = 0.0;
  1145.  
  1146.   type plottype = (NONE, TIME, SPECTRAL);
  1147.  
  1148.   var count:integer;
  1149.       reading:integer;
  1150.       dummy:integer;
  1151.       max_x,max_y:integer;
  1152.       max_count:integer;
  1153.       hour,minute,sec,sec100:word;
  1154.       last_plot:plottype;
  1155.       ch:char;
  1156.       s:string;
  1157.  
  1158.     function scale_y(reading:integer):integer;
  1159.     begin
  1160.       scale_y:=round((reading * SCALE - Y_INIT) * max_y / Y_SCALE);
  1161.     end;
  1162.  
  1163.     procedure init_graph;
  1164.     var y:integer;
  1165.  
  1166.       procedure draw_tick(reading:integer; db:string);
  1167.       var y:integer;
  1168.       begin
  1169.         y:=scale_y(reading);
  1170.         moveto(X_AXIS-2,y);
  1171.         lineto(X_AXIS+5,y);
  1172.         setcolor(8);
  1173.         lineto(max_x,y);
  1174.         setcolor(15);
  1175.         moveto(X_AXIS-30,y-3);
  1176.         outtext(db);
  1177.       end;
  1178.  
  1179.     begin
  1180.       setgraphmode(graphmode);
  1181.       moveto(X_AXIS,scale_y(240));
  1182.       lineto(X_AXIS,scale_y(60));
  1183.       draw_tick(67,'');
  1184.       draw_tick(71,'+50');
  1185.       draw_tick(75,'');
  1186.       draw_tick(81,'+30');
  1187.       draw_tick(87,'');
  1188.       draw_tick(92,'+10');
  1189.       draw_tick(99,'+9');
  1190.       draw_tick(108,'+7');
  1191.       draw_tick(118,'+5');
  1192.       draw_tick(133,'+3');
  1193.       draw_tick(154,'+1');
  1194.     end;
  1195.  
  1196.     procedure plot_title(s:string);
  1197.     var offset:integer;
  1198.     begin
  1199.       { compensate for low res displays to allow room for text }
  1200.       case graphmode of
  1201.         CGAHI,MCGAHI: offset:=0;
  1202.                VGAHI: offset:=15;
  1203.                  else offset:=10;
  1204.         end;
  1205.       setfillstyle(1,0);
  1206.       bar(100,18 + offset,max_x,25 + offset);
  1207.       moveto(((max_x - length(s)) div 2) - 60,18 + offset);
  1208.       outtext(S);
  1209.     end;
  1210.  
  1211.     procedure clear_prompt;
  1212.     begin
  1213.       setfillstyle(1,0);
  1214.       bar(1,1,max_x,8);
  1215.       setcolor(2);
  1216.       moveto(1,1);
  1217.     end;
  1218.  
  1219.     procedure out_prompt(s:string);
  1220.     begin
  1221.       clear_prompt;
  1222.       outtext(s);
  1223.       setcolor(15);
  1224.     end;
  1225.  
  1226.     procedure main_prompt;
  1227.     begin
  1228.       out_prompt('GRAPHICS: C(lear, T(ime plot, S(pectral plot, Q(uit');
  1229.     end;
  1230.  
  1231.     procedure time_plot;
  1232.     var ch:char;
  1233.         time_stamp,old_time_stamp:integer;
  1234.     begin
  1235.       old_time_stamp:=0;
  1236.       out_prompt('Hit <SPACE BAR> to stop');
  1237.       setfillstyle(1,0);
  1238.       bar(1,scale_y(238),getmaxx,scale_y(260));
  1239.       plot_title('T I M E  P L O T');
  1240.       count:=0;
  1241.       remote_on;
  1242.       reading:=get_s_reading;
  1243.       moveto(X_INIT,scale_y(reading));
  1244.       setcolor(11);
  1245.       while not keypressed do
  1246.         begin
  1247.           gettime(hour,minute,sec,sec100);
  1248.           time_stamp:=sec;
  1249.           if (time_stamp <> old_time_stamp) then
  1250.             begin
  1251.               old_time_stamp:=time_stamp;
  1252.               inc(count);
  1253.             end;
  1254.           reading:=get_s_reading;
  1255.           lineto(X_INIT + count, scale_y(reading));
  1256.           if count > max_count then
  1257.             begin
  1258.               count:=0;
  1259.               moveto(X_INIT,scale_y(reading));
  1260.             end;
  1261.         end;
  1262.       main_prompt;
  1263.       ch:=fetch; { get key pressed and discard }
  1264.     end;
  1265.  
  1266.     procedure spectral_plot;
  1267.     const BUFFERSIZE = 50;
  1268.  
  1269.     var ok,nullval:boolean;
  1270.         start,stop:integer;
  1271.         delta:real;
  1272.         last_freq, freq:real;
  1273.         freq_range:real;
  1274.         count_delta,count:integer;
  1275.         i:integer;
  1276.         s:string;
  1277.         freq_buffer: array[1..BUFFERSIZE] of byte;
  1278.         plot_cnt: buffer_type;
  1279.         old_stat:receivertype;
  1280.         ch:char;
  1281.  
  1282.       procedure draw_x_axis;
  1283.       const POINTS = 8;
  1284.       var i,x:integer;
  1285.           del:real;
  1286.           s:string;
  1287.           f:real;
  1288.       begin
  1289.         moveto(X_AXIS,scale_y(240));
  1290.         lineto(max_x, scale_y(240));
  1291.         del:=max_count / POINTS;
  1292.         for i:=0 to POINTS do
  1293.           begin
  1294.             x:=round(X_AXIS + del * i);
  1295.             moveto(x, scale_y(242));
  1296.             lineto(x, scale_y(238));
  1297.             moveto(x - 26, scale_y(250));
  1298.             f:=start_freq + i * ((stop_freq - start_freq) / POINTS);
  1299.             str(f:7:1,s);
  1300.             outtext(s);
  1301.           end;
  1302.       end;
  1303.  
  1304.       procedure radio_setup;
  1305.       var s:string;
  1306.       begin
  1307.         old_stat:=receiverstat;
  1308.         remote_on; { lock radio; cmd mode }
  1309.         set_mode(CW);
  1310.         set_bandwidth(NARR);
  1311.         if freq_range <= 100.0 then s:='1' else s:='2';
  1312.         set_tuning_rate(s);
  1313.         set_agc(FAST);
  1314.         set_freq(start_freq);
  1315.         delay(200);
  1316.         remote_off(0);
  1317.       end;
  1318.  
  1319.       procedure get_scan_range;
  1320.       begin
  1321.         start:=round(start_freq);
  1322.         str(start_freq:5:0,s);
  1323.         s:='starting frequency [default=' + s + ']';
  1324.         entnum(1,5,start,ok,nullval,s);
  1325.         if not ok then exit;
  1326.         if not nullval then start_freq:=start;
  1327.         stop:=round(stop_freq);
  1328.         str(stop_freq:5:0,s);
  1329.         s:='stopping frequency [default=' + s + ']';
  1330.         entnum(1,7,stop,ok,nullval,s);
  1331.         if not ok then exit;
  1332.         if not nullval then stop_freq:=stop;
  1333.       end;
  1334.  
  1335.       procedure restore_radio; { to settings prior to spectral plot }
  1336.       begin
  1337.         remote_on;
  1338.         with receiverstat do
  1339.           set_all(199,attenuator,bandwidth,mode,frequency,agc);
  1340.         set_tuning_rate('0'); { 1 Hz }
  1341.         delay(100);
  1342.         remote_off(REMOTE_DLY);
  1343.       end;
  1344.  
  1345.       procedure plot_point(freq:real; y:byte);
  1346.       var x,ave:integer;
  1347.           color:integer;
  1348.       begin
  1349.         { adaptively Kalman filter reading to get statistical average }
  1350.         {   the idea is to end up with a running average where the last }
  1351.         {   point has no more influence than the first  }
  1352.         x:=round(max_count * ((freq - start_freq) / freq_range));
  1353.         ave:=round((plot_cnt[x] * plot_buffer[x] + y) / (plot_cnt[x] + 1));
  1354.         plot_buffer[x]:=ave;
  1355.         if plot_cnt[x] < 255 then inc(plot_cnt[x]);
  1356.  
  1357.         { now draw point }
  1358.         if y < 99 then color:=12 else color:=11;
  1359.         putpixel(x + X_AXIS,scale_y(y),color);
  1360.       end;
  1361.  
  1362.       procedure draw_average;
  1363.       var x:integer;
  1364.       begin
  1365.         moveto(X_AXIS,scale_y(plot_buffer[0]));
  1366.         setcolor(14);
  1367.         for x:=1 to max_count do if plot_buffer[x] > 0 then
  1368.           lineto(x + X_AXIS,scale_y(plot_buffer[x]));
  1369.       end;
  1370.  
  1371.       procedure do_find(var ch:char);
  1372.       const CURSOR_HEIGHT = 10;
  1373.             TAB_COUNT     = 25;
  1374.       type shadetype = (NORMAL,HIGHLIGHT);
  1375.       var x:integer;
  1376.           p:pointer;
  1377.           f:real;
  1378.           keyfound:boolean;
  1379.           last_x:integer;
  1380.  
  1381.         procedure plot_point(x:integer; shade:shadetype);
  1382.         var color:integer;
  1383.  
  1384.           procedure hash_mark;
  1385.           var ypos:integer;
  1386.           begin
  1387.             { insure that exact cursor location is found }
  1388.             if shade = NORMAL then x:=last_x;
  1389.  
  1390.             { get average point. set to x-axis if not plotted yet }
  1391.             if plot_buffer[x] > 0
  1392.               then ypos:=scale_y(plot_buffer[x])
  1393.               else ypos:=scale_y(240);
  1394.  
  1395.             if shade = NORMAL then { remove cursor }
  1396.               putimage(x + X_AXIS - CURSOR_HEIGHT,
  1397.                        ypos - CURSOR_HEIGHT, p^, normalput)
  1398.             else
  1399.               begin
  1400.                 { save image under cursor }
  1401.                 getimage(x + X_AXIS - CURSOR_HEIGHT,ypos - CURSOR_HEIGHT
  1402.                         ,x + X_AXIS + CURSOR_HEIGHT,ypos + CURSOR_HEIGHT, p^);
  1403.  
  1404.                 { draw cursor }
  1405.                 moveto(x + X_AXIS, ypos + CURSOR_HEIGHT);
  1406.                 lineto(x + X_AXIS, ypos - CURSOR_HEIGHT);
  1407.                 moveto(x + X_AXIS, ypos);
  1408.                 lineto(x + X_AXIS - CURSOR_HEIGHT, ypos);
  1409.                 moveto(x + X_AXIS, ypos);
  1410.                 lineto(x + X_AXIS + CURSOR_HEIGHT, ypos);
  1411.               end;
  1412.             last_x:=x;
  1413.           end;
  1414.  
  1415.         begin
  1416.           case shade of
  1417.             NORMAL:    begin
  1418.                          color:=14;
  1419.                          setcolor(0);
  1420.                        end;
  1421.             HIGHLIGHT: begin
  1422.                          color:=10;
  1423.                          setcolor(10);
  1424.                        end;
  1425.           end;
  1426.           hash_mark;
  1427.         end;
  1428.  
  1429.         procedure clear_receiver_status;
  1430.         var offset:integer;
  1431.         begin
  1432.  
  1433.           { compensate for low res displays to allow room for text }
  1434.           case graphmode of
  1435.             CGAHI,MCGAHI: offset:=0;
  1436.                    VGAHI: offset:=40;
  1437.                      else offset:=20;
  1438.           end;
  1439.  
  1440.           setfillstyle(1,0);
  1441.           bar(100,25 + offset,max_x,36 + offset);
  1442.           moveto(130,29 + offset);
  1443.         end;
  1444.  
  1445.         function freq_to_x(f:real):integer;
  1446.         { convert frequency to x coordinate }
  1447.         var x:integer;
  1448.         begin
  1449.           x:=round(max_count * ((f - start_freq) / freq_range));
  1450.           if x < 0 then x:=0;
  1451.           if x > max_count then x:=max_count;
  1452.           freq_to_x:=x;
  1453.         end;
  1454.  
  1455.         procedure show_receiver;
  1456.         { display receiver status at the top of the screen }
  1457.         var s,s1:string;
  1458.         begin
  1459.           with receiverstat do
  1460.             begin
  1461.               str(frequency:8:2,s1);
  1462.               s:='Frequency: ' + s1 +
  1463.                '  Mode: ' + mode_to_str(mode) +
  1464.                '  BW: ' + bandwidth_to_str(bandwidth) +
  1465.                '  AGC: ' + agc_to_str(agc);
  1466.             end;
  1467.           clear_receiver_status;
  1468.           setcolor(3);
  1469.           outtext(s);
  1470.         end;
  1471.  
  1472.         procedure tune_radio(f:real);
  1473.         begin
  1474.           { remove cursor from old frequency }
  1475.           x:=freq_to_x(receiverstat.frequency);
  1476.           plot_point(x,NORMAL);
  1477.  
  1478.           { set the frequency }
  1479.           remote_on;
  1480.           set_freq(f);
  1481.           delay(100);
  1482.           remote_off(100);
  1483.           toggle_remote;
  1484.           show_receiver;
  1485.           { move cursor }
  1486.           x:=freq_to_x(f);
  1487.           plot_point(x, HIGHLIGHT);
  1488.         end;
  1489.  
  1490.         procedure inc_freq;
  1491.         var f:real;
  1492.         begin
  1493.           f:=trunc(receiverstat.frequency/10.0) * 10.0;
  1494.           if receiverstat.frequency - f >= 5.0 then f:=f + 5.0;
  1495.           f:=f + 5.0;
  1496.           if f > stop_freq then f:=stop_freq;
  1497.           tune_radio(f);
  1498.         end;
  1499.  
  1500.         procedure dec_freq;
  1501.         var f:real;
  1502.         begin
  1503.           f:=trunc(receiverstat.frequency/10.0) * 10.0;
  1504.           if receiverstat.frequency - f > 5.0 then f:=f + 10.0
  1505.           else if receiverstat.frequency - f > 0.0 then f:=f + 5.0;
  1506.           f:=f - 5.0;
  1507.           if f < start_freq then f:=start_freq;
  1508.           tune_radio(f);
  1509.         end;
  1510.  
  1511.         procedure move_right(var keyfound:boolean;var ch:char; count:integer);
  1512.         var f:real;
  1513.         begin
  1514.           { unload the key buffer if its full to speed this up }
  1515.           while keypressed and (ch in [RIGHTARROW, TAB]) do
  1516.             begin
  1517.               ch:=upcase(fetch);
  1518.               case ch of
  1519.                 RIGHTARROW: inc(count);
  1520.                 TAB:        count:=count + TAB_COUNT;
  1521.               else keyfound:=TRUE;
  1522.               end;
  1523.             end;
  1524.  
  1525.           f:=receiverstat.frequency + delta * count;
  1526.           if f > stop_freq then f:=stop_freq;
  1527.           tune_radio(f);
  1528.         end;
  1529.  
  1530.         procedure move_left(var keyfound:boolean;var ch:char; count:integer);
  1531.         var f:real;
  1532.         begin
  1533.           { unload the key buffer if its full to speed this up }
  1534.           while keypressed and (ch in [LEFTARROW, BACKTAB]) do
  1535.             begin
  1536.               ch:=upcase(fetch);
  1537.               case ch of
  1538.                 LEFTARROW: inc(count);
  1539.                 BACKTAB:   count:=count + TAB_COUNT;
  1540.               else keyfound:=TRUE;
  1541.               end;
  1542.             end;
  1543.           f:=receiverstat.frequency - delta * count;
  1544.           if f < start_freq then f:=start_freq;
  1545.           tune_radio(f);
  1546.         end;
  1547.  
  1548.       begin
  1549.         out_prompt('Find: C(ontinue, L(og, Q(uit');
  1550.         draw_average;
  1551.         if not SPECTRAL_MODE then
  1552.           begin
  1553.             receiverstat:=old_stat;
  1554.             restore_radio;
  1555.           end;
  1556.         toggle_remote;
  1557.         show_receiver;
  1558.  
  1559.         { init buffer for saving image under graphics cursor }
  1560.         getmem(p,imagesize(100 - CURSOR_HEIGHT, 100 - CURSOR_HEIGHT
  1561.                           ,100 + CURSOR_HEIGHT, 100 + CURSOR_HEIGHT));
  1562.         x:=freq_to_x(receiverstat.frequency);
  1563.         plot_point(x, HIGHLIGHT);
  1564.         repeat
  1565.           repeat
  1566.             if async_buffer_check(ch) then { they touched the dial, etc }
  1567.               begin
  1568.                 { remove old cursor }
  1569.                 x:=freq_to_x(receiverstat.frequency);
  1570.                 plot_point(x, NORMAL);
  1571.  
  1572.                 check_status(s); { get new receiver status }
  1573.  
  1574.                 { empty queue to compensate for graph drawing delay }
  1575.                 while async_buffer_check(ch) do check_status(s);
  1576.  
  1577.                 show_receiver;
  1578.  
  1579.                 { move cursor }
  1580.                 x:=freq_to_x(receiverstat.frequency);
  1581.                 plot_point(x, HIGHLIGHT);
  1582.               end;
  1583.           until keypressed;
  1584.           ch:=upcase(fetch);
  1585.           keyfound:=FALSE;
  1586.           repeat
  1587.             case ch of
  1588.               RIGHTARROW: move_right(keyfound, ch, 1);
  1589.               LEFTARROW:  move_left(keyfound, ch, 1);
  1590.               TAB:        move_right(keyfound, ch, 25);
  1591.               BACKTAB:    move_left(keyfound, ch, 25);
  1592.               '>' ,'.':   begin
  1593.                             inc_mode;
  1594.                             show_receiver;
  1595.                           end;
  1596.               '<', ',':   begin
  1597.                             dec_mode;
  1598.                             show_receiver;
  1599.                           end;
  1600.               ']':        begin
  1601.                             inc_bandwidth;
  1602.                             show_receiver;
  1603.                           end;
  1604.               '[':        begin
  1605.                             dec_bandwidth;
  1606.                             show_receiver;
  1607.                           end;
  1608.               '+':        inc_freq;
  1609.               '-':        dec_freq;
  1610.             end;
  1611.           until not keyfound;
  1612.         until ch in ['C','L','Q'];
  1613.  
  1614.         { remove cursor before leaving }
  1615.         x:=freq_to_x(receiverstat.frequency);
  1616.         plot_point(x,NORMAL);
  1617.  
  1618.         SPECTRAL_MODE:=ch = 'L';
  1619.         old_stat:=receiverstat;
  1620.         clear_receiver_status;
  1621.         if ch = 'C' then
  1622.           begin
  1623.             radio_setup;
  1624.             remote_on;
  1625.             set_freq(last_freq);
  1626.             remote_off(0);
  1627.           end;
  1628.         out_prompt('COMMANDS: C(lear, F(ind, Q(uit');
  1629.         dispose(p);
  1630.       end;
  1631.  
  1632.       procedure init_spectral_plot;
  1633.       begin
  1634.         init_graph;
  1635.         out_prompt('COMMANDS: A(verage, C(lear, F(ind, Q(uit');
  1636.         plot_title('S P E C T R A L  P L O T');
  1637.         draw_x_axis;
  1638.       end;
  1639.  
  1640.     begin
  1641.       if not SPECTRAL_MODE then
  1642.         begin
  1643.           restorecrtmode;
  1644.           home;
  1645.           writea(LIGHTGREEN,FOREGROUND);
  1646.           writeln(output,' SPECTRAL PLOT: Enter the frequency range to scan');
  1647.           get_scan_range;
  1648.         end;
  1649.       init_spectral_plot;
  1650.       freq_range:=stop_freq - start_freq;
  1651.       if not SPECTRAL_MODE then radio_setup;
  1652.  
  1653.       { init plot statistics used to Kalman filter averages }
  1654.       for i:=0 to max_count do
  1655.         begin
  1656.           if not SPECTRAL_MODE then plot_buffer[i]:=0;
  1657.           plot_cnt[i]:=0;
  1658.         end;
  1659.  
  1660.       last_freq:=start_freq;
  1661.       ch:=' ';
  1662.       if SPECTRAL_MODE then do_find(ch);
  1663.       if not SPECTRAL_MODE and (ch <> 'Q') then repeat
  1664.         remote_on;
  1665.         delay(1000);
  1666.         count:=0;
  1667.         set_auto_tune('+');
  1668.         
  1669.         { gather data }
  1670.         while (count < BUFFERSIZE) do
  1671.           begin
  1672.             inc(count);
  1673.             freq_buffer[count]:=get_s_reading;
  1674.           end;
  1675.         remote_off(0);
  1676.         toggle_remote;
  1677.         delay(300);
  1678.         while async_buffer_check(ch) do check_status(s); { get frequency }
  1679.         freq:=receiverstat.frequency;
  1680.         if freq > stop_freq then
  1681.           begin
  1682.             remote_on;
  1683.             set_freq(start_freq);
  1684.             delay(1000);
  1685.           end;
  1686.         remote_off(0);
  1687.         delta:=(freq - last_freq) / BUFFERSIZE;
  1688.  
  1689.         { plot buffer contents }
  1690.         count:=0;
  1691.         freq:=last_freq;
  1692.         while (freq < stop_freq) and (count < BUFFERSIZE) do
  1693.           begin
  1694.             inc(count);
  1695.             plot_point(freq, freq_buffer[count]);
  1696.             freq:=freq + delta;
  1697.           end;
  1698.         last_freq:=receiverstat.frequency;
  1699.         if last_freq > stop_freq then last_freq:=start_freq;
  1700.         while keypressed do
  1701.           begin
  1702.              ch:=upcase(fetch);
  1703.              case ch of
  1704.                'F': do_find(ch);
  1705.                'A': draw_average;
  1706.                'C': init_spectral_plot;
  1707.              end;
  1708.           end;
  1709.       until (ch = 'Q') or SPECTRAL_MODE;
  1710.       if not SPECTRAL_MODE then
  1711.         begin
  1712.           receiverstat:=old_stat;
  1713.           restore_radio;
  1714.         end;
  1715.       main_prompt;
  1716.     end;
  1717.  
  1718.   begin
  1719.     if radio_type <> 535 then exit;
  1720.     max_x:=getmaxx - 30; max_y:=getmaxy;
  1721.     max_count:=max_x - X_AXIS;
  1722.     last_plot:=NONE;
  1723.     if SPECTRAL_MODE then spectral_plot
  1724.     else
  1725.       begin
  1726.         init_graph;
  1727.         main_prompt;
  1728.         start_freq:=receiverstat.frequency - 5.0;
  1729.         stop_freq :=receiverstat.frequency + 5.0;
  1730.       end;
  1731.     if not SPECTRAL_MODE then
  1732.       begin
  1733.         repeat
  1734.           if keypressed then ch:=upcase(fetch) else ch:='@' { nop };
  1735.           case ch of
  1736.             '@':; { nop }
  1737.             'C':begin
  1738.                   init_graph;
  1739.                   main_prompt;
  1740.                 end;
  1741.             'T':time_plot;
  1742.             'S':spectral_plot;
  1743.           end;
  1744.         until (ch = 'Q') or SPECTRAL_MODE;
  1745.       end;
  1746.     restorecrtmode;
  1747.     init_crt;
  1748.     remote_off(0); { unlock radio }
  1749.     if async_buffer_check(ch) then comreadln(COM_NRD,s);
  1750.     information_mode_on;
  1751.     gotoxy(x_pos,y_pos);
  1752.     show_log(rec,TRUE,TRUE);
  1753.   end;
  1754.  
  1755. begin
  1756. end.